1 module hip.hipaudio.backend.openal.source; 2 version(OpenAL): 3 4 import hip.hipaudio.backend.openal.clip; 5 import hip.error.handler; 6 import hip.hipaudio.audio; 7 import hip.hipaudio.audiosource; 8 import hip.util.memory; 9 import hip.hipaudio.backend.openal.al_err; 10 import hip.hipaudio.backend.audioclipbase; 11 import bindbc.openal; 12 13 14 /** 15 * Constant used for making the panning distance offset from the listener 16 */ 17 enum ALfloat PANNING_CONSTANT = 1000; 18 19 public class HipOpenALAudioSource : HipAudioSource 20 { 21 import hip.console.log; 22 //id is created from OpenAL player 23 uint id; 24 bool isStreamed; 25 26 this(bool isStreamed) 27 { 28 this.isStreamed=isStreamed; 29 alGenSources(1, &id); 30 alCheckError("Error creating OpenAL source"); 31 alSourcef(id, AL_GAIN, 1); 32 alSourcef(id, AL_PITCH, 1); 33 alSource3f(id, AL_POSITION, 0f, 0f, 0f); 34 alSource3f(id, AL_VELOCITY, 0f, 0f, 0f); 35 alSourcei(id, AL_LOOPING, AL_FALSE); 36 alCheckError("Error setting OpenAL source properties"); 37 } 38 39 alias pitch = AHipAudioSource.pitch; 40 alias panning = AHipAudioSource.panning; 41 alias volume = AHipAudioSource.volume; 42 alias pitch = AHipAudioSource.pitch; 43 alias clip = HipAudioSource.clip; 44 alias position = AHipAudioSource.position; 45 alias loop = AHipAudioSource.loop; 46 47 48 override float pitch(float value) 49 { 50 auto ret = super.pitch(value); 51 if(isDirty && isPlaying) 52 { 53 alSourcef(id, AL_PITCH, ret); 54 alCheckError("Error setting OpenAL source pitch"); 55 isDirty = false; 56 } 57 return ret; 58 } 59 60 override float panning(float value) 61 { 62 auto ret = super.panning(value); 63 if(isDirty && isPlaying) 64 { 65 isDirty = false; 66 alSource3f(id, AL_POSITION, position[0] + (ret*PANNING_CONSTANT), position[1], position[2]); 67 alCheckError("Error setting OpenAL source position/panning"); 68 } 69 return ret; 70 } 71 override float volume(float value) 72 { 73 auto ret = super.volume(value); 74 if(isDirty && isPlaying) 75 { 76 isDirty = false; 77 alSourcef(id, AL_GAIN, ret); 78 alCheckError("Error setting OpenAL source volume"); 79 } 80 return ret; 81 } 82 83 override float[3] position(float[3] value) 84 { 85 auto ret = super.position(value); 86 if(isDirty && isPlaying) 87 { 88 isDirty = false; 89 alSource3f(id, AL_POSITION, ret[0] + (panning*PANNING_CONSTANT), ret[1], ret[2]); 90 alCheckError("Error setting OpenAL source position/panning"); 91 } 92 return ret; 93 } 94 95 override bool loop(bool value) 96 { 97 bool ret = super.loop(value); 98 if(isDirty && isPlaying) 99 { 100 alSourcei(id, AL_LOOPING, ret ? AL_TRUE : AL_FALSE); 101 alCheckError("Error setting OpenAL loop"); 102 } 103 return ret; 104 } 105 106 public void setDistanceModel(DistanceModel model) 107 { 108 alDistanceModel(getALDistanceModel(model)); 109 alCheckError("Error setting OpenAL source distance model"); 110 } 111 /** 112 * After the max distance, the volume won't decrease anymore 113 */ 114 public void setMaxDistance(float dist) 115 { 116 alSourcef(id, AL_MAX_DISTANCE, dist); 117 alCheckError("Error setting OpenAL source max distance"); 118 } 119 120 /** 121 * Sets the distance where the volume will be equal to 1 122 */ 123 void setReferenceDistance(float dist) 124 { 125 alSourcef(id, AL_REFERENCE_DISTANCE, dist); 126 alCheckError("Error setting OpenAL source reference distance"); 127 } 128 /** 129 * The factor which the sound volume decreases when the distance is greater 130 * than the reference 131 */ 132 public void setRolloffFactor(float factor) 133 { 134 alSourcef(id, AL_ROLLOFF_FACTOR, factor); 135 alCheckError("Error setting OpenAL source rolloff factor"); 136 } 137 138 public void setVelocity(in float[3] vel) 139 { 140 alSource3f(id, AL_VELOCITY, vel[0], vel[1], vel[2]); 141 alCheckError("Error setting OpenAL source velocity"); 142 143 } 144 void setDoppler(in float[3] vel) 145 { 146 alSource3f(id, AL_DOPPLER_VELOCITY, vel[0], vel[1], vel[2]); 147 alCheckError("Error setting OpenAL source doppler factor"); 148 } 149 150 override bool play() 151 { 152 HipOpenALClip clp = clip.getAudioClipBackend!(HipOpenALClip); 153 if(clp.hasBuffer) 154 { 155 if(isDirty) 156 { 157 isDirty = false; 158 alSourcef(id, AL_PITCH, pitch); 159 alCheckError("Error setting OpenAL source pitch"); 160 alSourcef(id, AL_GAIN, volume); 161 alCheckError("Error setting OpenAL source volume"); 162 alSource3f(id, AL_POSITION, position[0] + (panning*PANNING_CONSTANT), position[1], position[2]); 163 alCheckError("Error setting OpenAL source position/panning"); 164 alSourcei(id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE); 165 alCheckError("Error setting OpenAL loop"); 166 } 167 alSourcePlay(id); 168 alCheckError("Error querying OpenAL play"); 169 return true; 170 } 171 else 172 ErrorHandler.showErrorMessage("Tried to play without a buffer", ""); 173 return false; 174 } 175 176 public bool resume() 177 { 178 if(!isPlaying) 179 { 180 alSourcePlay(id); 181 alCheckError("Error querying OpenAL play"); 182 return true; 183 } 184 return false; 185 } 186 187 188 override bool stop() 189 { 190 alSourceStop(id); 191 alCheckError("Error querying OpenAL stop"); 192 return false; 193 } 194 195 override bool pause() 196 { 197 alSourcePause(id); 198 alCheckError("Error querying OpenAL pause"); 199 return false; 200 } 201 override bool play_streamed(){return play();} 202 203 204 override IHipAudioClip clip(IHipAudioClip newClip) 205 { 206 super.clip(newClip); 207 // if(!newClip.isStreamed) 208 // { 209 // HipAudioBuffer buf = getBufferFromAPI(newClip); 210 // alSourcei(id, AL_BUFFER, buf.al); 211 // } 212 // else 213 { 214 HipAudioBuffer buf = getBufferFromAPI(newClip); //use clip.chunkSize in future 215 alSourceQueueBuffers(id, 1, &buf.al); 216 } 217 logln(id); 218 return newClip; 219 } 220 221 222 override void pullStreamData() 223 { 224 ErrorHandler.assertExit(clip !is null, "Can't pull stream data without any buffer attached"); 225 ErrorHandler.assertExit(id != 0, "Can't pull stream data without source id"); 226 227 HipAudioBuffer buffer; 228 int processed; 229 alGetSourcei(id, AL_BUFFERS_PROCESSED, &processed);//Gets the queueId 230 buffer.al = cast(uint)processed; 231 if(buffer.al != 0) 232 { 233 //Returns the bufferId to freeBuf 234 alSourceUnqueueBuffers(id, 1, &buffer.al); 235 (cast(HipAudioClip)this.clip).setBufferAvailable(buffer); 236 } 237 clip.updateStream(); 238 239 HipOpenALClip c = cast(HipOpenALClip)clip; 240 buffer = c.getBuffer(c.getClipData(), c.chunkSize); 241 alSourceQueueBuffers(id, 1, &buffer.al); 242 243 } 244 245 246 uint getALFreeBuffer() 247 { 248 int b; 249 alGetSourcei(id, AL_BUFFERS_PROCESSED, &b); 250 return cast(uint)b; 251 } 252 253 override HipAudioBufferWrapper* getFreeBuffer() 254 { 255 HipAudioBuffer buffer; 256 int b; 257 alGetSourcei(id, AL_BUFFERS_PROCESSED, &b); 258 buffer.al = cast(uint)b; 259 if(b == 0) 260 return null; 261 return (cast(HipAudioClip)clip).findBuffer(buffer); 262 } 263 264 265 266 ~this() 267 { 268 logln("HipAudioSource Killed!"); 269 alDeleteSources(1, &id); 270 id = 0; 271 } 272 } 273 274 ALenum getALDistanceModel(DistanceModel model) 275 { 276 final switch(model) with(DistanceModel) 277 { 278 case DISTANCE_MODEL: return AL_DISTANCE_MODEL; 279 case INVERSE: return AL_INVERSE_DISTANCE; 280 case INVERSE_CLAMPED: return AL_INVERSE_DISTANCE_CLAMPED; 281 case LINEAR: return AL_LINEAR_DISTANCE; 282 case LINEAR_CLAMPED: return AL_LINEAR_DISTANCE_CLAMPED; 283 case EXPONENT: return AL_EXPONENT_DISTANCE; 284 case EXPONENT_CLAMPED: return AL_EXPONENT_DISTANCE_CLAMPED; 285 } 286 }